package it.sd.pwframework.persistence.common;



import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.CollectionType;
import org.hibernate.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository("commonDao")
public class CommonSession {

	@Autowired
	private SessionFactory sessionFactory;

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Session getCurrentSession() {
		Session session = sessionFactory.getCurrentSession();
		return session;
	}

	public void flush() {
		getCurrentSession().flush();
	}

	public void persist(Object entity) {
		getCurrentSession().persist(entity);
	}

	public void testMetadata() {
		System.out.println("****************************************");

		Map<String, ClassMetadata> map = sessionFactory.getAllClassMetadata();
		for (ClassMetadata cmd : map.values()) {
			
			System.out.println("class metadata: " + cmd.getClass());
			
//			TypeHelper th =  sessionFactory.getTypeHelper();
//			th.

			String[] names 		= cmd.getPropertyNames();
			boolean[] laziness	= cmd.getPropertyLaziness();
			Type[] types 		= cmd.getPropertyTypes();
			boolean[] nullable   = cmd.getPropertyNullability();
			for (int i = 0; i < names.length; i++) {
				
				System.out.println(names[i] + ": " + types[i].getName() + " " + laziness[i] + " " + nullable[i]);
				try {
					Field dimWidthField = cmd.getMappedClass().getDeclaredField(names[i]);
					Column columnAnnotation = dimWidthField.getAnnotation(Column.class);
					if (columnAnnotation!=null) {
						System.out.println(columnAnnotation.length()); // 5
						System.out.println(columnAnnotation.precision()); // 0
					}
				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		System.out.println(sessionFactory.getAllCollectionMetadata());

		System.out.println("****************************************");

	}
	
	public Collection<ClassMetadata> listEntities() {
		return sessionFactory.getAllClassMetadata().values();
	}

	public ClassMetadata getEntity(String name) {
		return sessionFactory.getClassMetadata(name);
	}


	@SuppressWarnings("rawtypes")
	public List findAll(String name) {
		String query = "select e from " + name +" e ";
		Query q = getCurrentSession().createQuery(query);
		return q.list();
	}


	public Object findById(String name, Long id) {
		String query = "select e from " + name +" e "
				+ "where e.id = :id";
		Query q = getCurrentSession().createQuery(query);
		setQueryParameter(q, "id", id);
		return q.uniqueResult();
	}

	public List findByProperties(Class name, Map<String, Object> properties) {
		
		Criteria criteria = getCurrentSession().createCriteria(name);
		
		if (properties!=null) {
			for (String key : properties.keySet()) {
				criteria.add(Restrictions.eq(key, properties.get(key)));		
			}
		}

		return criteria.list();
	}

	public void delete(String name, Long id) {
		Object o = findById(name, id);
		getCurrentSession().delete(o);
	}

	public void saveOrUpdate(Object object) throws Exception {
		getCurrentSession().saveOrUpdate(object);
	}

	protected void setQueryParameter(Query query, String paramName, Object value) throws HibernateException {

		if (value instanceof Collection) {
			query.setParameterList(paramName, (Collection<?>) value);
		} else if (value instanceof Object[]) {
			query.setParameterList(paramName, (Object[]) value);
		} else {
			query.setParameter(paramName, value);
		}
	}
	
	public Object newInstance(String classname, Long id) {
		
		return sessionFactory.getClassMetadata(classname).instantiate(id, (SessionImplementor)getCurrentSession());
	}

	public String getAssociatedEntityName(CollectionType type) {
		return type.getAssociatedEntityName((SessionFactoryImplementor)sessionFactory);
	}

	public Object getPropertyValue(String classname, Object object, String propertyName) {
		
		return getEntity(classname).getPropertyValue(object, propertyName);
		
	}
	
	
}
